# coding=utf-8
# 全世界最帅的帅哥
import threading
import time


class IdWorkerUtil:
    def __init__(self, worker_id, datacenter_id, sequence=0):
        self.worker_id = worker_id
        self.datacenter_id = datacenter_id
        self.sequence = sequence

        self.twepoch = 1585644268888  # 起始时间戳，可以根据需要调整
        self.worker_id_bits = 5
        self.datacenter_id_bits = 5
        self.sequence_bits = 12

        self.max_worker_id = -1 ^ (-1 << self.worker_id_bits)
        self.max_datacenter_id = -1 ^ (-1 << self.datacenter_id_bits)

        self.worker_id_shift = self.sequence_bits
        self.datacenter_id_shift = self.sequence_bits + self.worker_id_bits
        self.timestamp_left_shift = self.sequence_bits + self.worker_id_bits + self.datacenter_id_bits
        self.sequence_mask = -1 ^ (-1 << self.sequence_bits)

        self.last_timestamp = -1

        if self.worker_id > self.max_worker_id or self.worker_id < 0:
            raise ValueError(f"worker Id can't be greater than {self.max_worker_id} or less than 0")
        if self.datacenter_id > self.max_datacenter_id or self.datacenter_id < 0:
            raise ValueError(f"datacenter Id can't be greater than {self.max_datacenter_id} or less than 0")

    def _next_id(self):
        timestamp = self._time_gen()
        if timestamp < self.last_timestamp:
            raise RuntimeError(
                f"Clock moved backwards. Refusing to generate id for {self.last_timestamp - timestamp} milliseconds")

        if self.last_timestamp == timestamp:
            self.sequence = (self.sequence + 1) & self.sequence_mask
            if self.sequence == 0:
                timestamp = self._til_next_millis(self.last_timestamp)
        else:
            self.sequence = 0

        self.last_timestamp = timestamp

        return ((timestamp - self.twepoch) << self.timestamp_left_shift) | \
            (self.datacenter_id << self.datacenter_id_shift) | \
            (self.worker_id << self.worker_id_shift) | \
            self.sequence

    def _til_next_millis(self, last_timestamp):
        timestamp = self._time_gen()
        while timestamp <= last_timestamp:
            timestamp = self._time_gen()
        return timestamp

    @staticmethod
    def _time_gen():
        return int(time.time() * 1000)

    def next_id(self):
        with threading.Lock():
            return self._next_id()


